package com.qire.other.thirdPlatform;

import android.content.Context;
import android.graphics.Color;
import android.os.Message;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.CycleInterpolator;

import com.mobile.auth.gatewayauth.AuthRegisterXmlConfig;
import com.mobile.auth.gatewayauth.AuthUIConfig;
import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper;
import com.mobile.auth.gatewayauth.PreLoginResultListener;
import com.mobile.auth.gatewayauth.TokenResultListener;
import com.mobile.auth.gatewayauth.ui.AbstractPnsViewDelegate;
import com.qire.other.R;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;

/**
 * 阿里手机一键快速登录认证平台 <br />
 * 任何 token 相关的API操作通过  PhoneNumberAuthHelper 发起 -> 阿里服务端处理 -> 通知 TokenResultListener -> 回掉给 messageHandle 处理 -> 通知 handleCallbackMap 结束。
 */
public class AliQuickLoginPlatform extends ThirdPlatform implements TokenResultListener {

    // 全局单利模式引用对象
    public final static AliQuickLoginPlatform QUICK = new AliQuickLoginPlatform();

    // API 密钥
    private final static String SECRET_KEY = "P+gNb1NXzIfFSf/aq0v78VTeDEyyqfyAYfoWcu4PheC8kFdZ1PK7Y1xkJxevRXRx1ETDP8ar0MDXGZhor2BAy2dDjjL0PS/kTkNewR7gCtpHmqW67SqyNtsI7sc9fe0XDCvIsnjBTVN9LtqKYDnb2LhoQ4C1jxc6Q3XeghSm5G5f8j+VEjhno9zT0mYXSVum+h7MMUbMmleCick9Be2HASDZRgsgdNH/OPYTpy349HfR6t8Tx0U0RjSz8TDuB1nQzaem1Huw5u4KPN1zvGeLNkco6uEDdW7+TY1rZ73fWQAH3vcGpSKQmg==";

    // handel消息标签：专门处理 TokenResultListener 回调的标签:成功
    private final static int MSG_TAG_TOKEN_HANDLE_SUCCESS = 1;
    // handel消息标签：专门处理 TokenResultListener 回调的标签：失败
    private final static int MSG_TAG_TOKEN_HANDLE_FAILED = 2;
    // handel消息标签：快捷登录处理标签
    private final static int MSG_TAG_OTHER_SHORTCUT = 3;

    // 处理不同 TokenResultListener 的回掉函数
    private final HashMap<String, HandleCallback> handleCallbackMap = new HashMap<>();

    // 阿里手机一键登录平台的助手，用于 token API 发起。
    private PhoneNumberAuthHelper authHelper;

    // 检查手机系统网络等环境是否支持快速登录，用于预检查，防止在唤起启动页时需要等待超时。
    private boolean isSupportQuickLogin = false;
    private String  failedSupportQuickLoginJson = "";

    // 是否正在进行一次快速登录检查
    private boolean isQuickLogin = false;

    //region    #授权页原生控件样式定义 begin
    private AuthUIConfig uiConfig = new AuthUIConfig.Builder()
            // 页面设置
            .setDialogBottom(true)
            .setDialogHeight(450)
            .setAuthPageActIn("other_dialog_bottom_in", "other_dialog_bottom_in")
            .setAuthPageActOut("other_dialog_bottom_exit","other_dialog_bottom_exit")
            .setPageBackgroundPath("other_ali_quick_login_layout_bottom")
            // 不需要的
            .setNavHidden(true)
            .setLogoHidden(true)
            .setSloganHidden(true)
            // 号码
            .setNumFieldOffsetY(0)
            .setNumberSize(25)
            .setNumberColor(Color.rgb(0x28, 0x28, 0x28))
            // 一键登录按钮
            .setLogBtnOffsetY(63)
            .setLogBtnWidth(292)
            .setLogBtnHeight(50)
            .setLogBtnText("本机号码一键登录")
            .setLogBtnTextSize(16)
            .setLogBtnTextColor(Color.rgb(0x42, 0x33, 0x00))
            .setLogBtnBackgroundPath("other_ali_quick_login_button_yellow")
            // 切换登录方式
            .setSwitchOffsetY(136)
            .setSwitchAccText("切换手机号 >")
            .setSwitchAccTextSize(14)
            .setSwitchAccTextColor(Color.rgb(0x33, 0x33, 0x33))
            // 协议
            .setPrivacyOffsetY_B(46)
            .setProtocolGravity(Gravity.LEFT)
            .setUncheckedImgPath("other_ali_quick_login_icon_agree_protocol_b")
            .setCheckedImgPath("other_ali_quick_login_icon_agree_protocol_a")
            .setPrivacyBefore("同意")
            .setAppPrivacyOne("奇热漫画用户协议", "")
            .setAppPrivacyTwo("\n隐私条款", "")
            .setPrivacyEnd("并授权奇热漫画获得本机号码")
            .setPrivacyTextSize(12)
            .setAppPrivacyColor(Color.rgb(0x99, 0x99, 0x99), Color.rgb(0xff, 0x8c, 0x04))
            .setLogBtnToastHidden(true)
            .create();
    //endregion #授权页原生控件样式定义 end

    // 协议提示气泡
    private View protocolBubble;
    // 协议布局
    private View protocolLayout;

    /**
     * 单例模式私有构造
     */
    private AliQuickLoginPlatform() {}

    //region    #核心消息投递逻辑 begin
    /**
     * 消息处理
     * @param msg
     */
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_TAG_TOKEN_HANDLE_SUCCESS : {
                if (msg.obj instanceof TokenResultData) {
                    TokenResultData resultData = (TokenResultData) msg.obj;
                    HandleCallback handleCallback = handleCallbackMap.get(resultData.code);
                    if (handleCallback != null) {
                        handleCallback.callback(resultData);
                    }
                }
                return;
            }
            case MSG_TAG_TOKEN_HANDLE_FAILED : {
                HandleCallback handleCallback = handleCallbackMap.get(跳转登录引导页);
                if (handleCallback != null) {
                    handleCallback.callback(msg.obj);
                }
                return;
            }
            case MSG_TAG_OTHER_SHORTCUT : {
                String tag = (String) msg.obj;
                HandleCallback handleCallback = handleCallbackMap.get(tag);
                if (handleCallback != null) {
                    handleCallback.callback(msg.obj);
                }
                return;
            }
        }
    }

    /**
     * TokenResultListener 监听成功的回掉
     * @param successJson {@link TokenResultData TokenResultData} 的 json 字串
     */
    @Override
    public void onTokenSuccess(String successJson) {
        System.out.println("一键登录：请求成功：" + successJson);

        Message msg = Message.obtain();
        msg.what = MSG_TAG_TOKEN_HANDLE_SUCCESS;
        msg.obj = new TokenResultData().parser(successJson);
        sendMessage(msg);
    }

    /**
     * TokenResultListener 监听失败的回掉
     * @param failedJson {@link TokenResultData TokenResultData} 的 json 字串
     */
    @Override
    public void onTokenFailed(String failedJson) {
        System.out.println("一键登录：请求失败：" + failedJson);

        Message msg = Message.obtain();
        msg.what = MSG_TAG_TOKEN_HANDLE_FAILED;
        msg.obj = new TokenResultData().parser(failedJson);
        sendMessage(msg);
    }

    /**
     * 添加处理 TokenResultListener 回掉处理
     * @param whatTag
     * @param callback
     * @return
     */
    public <T> AliQuickLoginPlatform addHandleCallback(String whatTag, HandleCallback<T> callback) {
        handleCallbackMap.put(whatTag, callback);
        return this;
    }
    //endregion #核心消息投递逻辑 end

    //region    #对外开放相关互动操作 begin
    /** 退出授权页 */
    public void finishLoginAuthActivity() {
        // 清理勾选状态
        this.isChecked = false;
        /**
         * 关闭授权⻚loading
         * sdk完成回调之后不会关闭loading，需要开发者主动调⽤hideLoginLoading关闭loading
         */
        authHelper.hideLoginLoading();
        /**
         * 退出授权认证⻚
         * sdk完成回调之后不会关闭授权⻚，需要开发者主动调⽤quitLoginPage退出授权⻚
         */
        authHelper.quitLoginPage();
    }

    /**
     * 返回默认上⽹卡运营商
     * @return CMCC、CUCC、CTCC
     */
    public String getCurrentCarrierName() {
        return authHelper.getCurrentCarrierName();
    }
    //endregion #对外开放相关互动操作 end

    //region    #初始化一键登录相关设置 begin
    boolean isChecked = false;

    /**
     * 通过指定的 View 遍历所有父控件是否存在指定 ID 的View并返回
     * @param view 给定 View
     * @param viewID 目标 ViewId
     * @return 返回目标 View 如果都不存在则返回 null
     */
    private View findViewByParent(View view, int viewID) {
        if(view == null) {
            return null;
        }

        View targetView;

        do {
            view = (View) view.getParent();
            if(view == null || !(view instanceof ViewGroup)) {
                return null;
            }
            targetView = view.findViewById(viewID);
        } while (targetView == null);

        return targetView;
    }

    /**
     * 添加自定义扩展布局
     */
    private void addCustomLayout() {
        // 添加关闭按钮
        authHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder()
                .setLayout(R.layout.layout_ali_quick_login_close, new AbstractPnsViewDelegate() {
                    @Override
                    public void onViewCreated(View view) {
                        //这⾥返回的View，不建议⽤强引⽤，如果要⽤，请及时释放，否则容易造成内存泄 漏
                        findViewById(R.id.otherAliQuickLoginClose).setOnClickListener(v -> {
                            finishLoginAuthActivity();
                        });
                    }
                }).build());
        // 添加其他快捷登录面板
        authHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder()
                .setLayout(R.layout.layout_ali_quick_login_other_shortcut, new AbstractPnsViewDelegate() {
                    @Override
                    public void onViewCreated(View view) {
                        //这⾥返回的View，不建议⽤强引⽤，如果要⽤，请及时释放，否则容易造成内存泄 漏
                        findViewById(R.id.otherAliQuickLoginWeChat).setOnClickListener(v -> {
                            if(isChecked) {
                                Message msg = Message.obtain();
                                msg.what = MSG_TAG_OTHER_SHORTCUT;
                                msg.obj = "WeChat";
                                sendMessage(msg);
                            } else {
                                runProtocolAnimation();
                            }
                        });
                        //这⾥返回的View，不建议⽤强引⽤，如果要⽤，请及时释放，否则容易造成内存泄 漏
                        findViewById(R.id.otherAliQuickLoginQQ).setOnClickListener(v -> {
                            if(isChecked) {
                                Message msg = Message.obtain();
                                msg.what = MSG_TAG_OTHER_SHORTCUT;
                                msg.obj = "QQ";
                                sendMessage(msg);
                            } else {
                                runProtocolAnimation();
                            }
                        });
                    }
                }).build());
        // 添加协议提示器泡
        authHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder()
                .setLayout(R.layout.layout_ali_quick_login_protocol_bubble, new AbstractPnsViewDelegate() {
                    @Override
                    public void onViewCreated(View view) {
                        view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                            @Override
                            public void onViewAttachedToWindow(View v) {
                                protocolBubble = findViewById(R.id.protocolBubble);
                                // 协议框和其他的ID被写在样式里了，有需要可以去 SDK 中找。
                                protocolLayout = findViewByParent(protocolBubble, R.id.authsdk_protocol_view);
                            }
                            @Override
                            public void onViewDetachedFromWindow(View v) {
                                protocolBubble = null;
                                protocolLayout = null;
                            }
                        });
                    }
                }).build());
    }

    private void runProtocolAnimation() {
        if(protocolBubble != null) {
            protocolBubble.setVisibility(View.VISIBLE);
            protocolBubble.postDelayed(() -> protocolBubble.setVisibility(View.GONE), 1000);
        }
        if(protocolLayout != null) {
            protocolLayout.animate().translationX(50).setDuration(500)
                    .setInterpolator(new CycleInterpolator(1f))
                    .start();
        }
    }

    /**
     * 一键登录原生控件点击事件处理
     * @param code 控件事件ID
     * @param cxt 上下文
     * @param json 附加数据的JSON
     */
    private void onUIControlClick(String code, Context cxt, String json) {
        System.out.println("阿里手机一键登录原生UI点击事件 : code = " + code + ", jsonObj = " + json);

        switch (code) {
            case "700002" : {
                if(!isChecked) {
                    runProtocolAnimation();
                }
                return;
            }
            case "700003" : {
                try {
                    JSONObject jsonObj = new JSONObject(json);
                    isChecked = jsonObj.getBoolean("isChecked");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始话 ali 平台相关细心，比如密钥，页面编辑等。
     * @param context
     */
    public synchronized AliQuickLoginPlatform init(Context context) {
        if(authHelper == null) {
            /**
             * TokenResultListener内存泄漏
             * sdk内部会持有外部设置进来的TokenResultListener，在⼀键登录功能使⽤完毕之后通过
             * PhoneNumberAuthHelper.setAuthListener(null)将回调置空即可。
             *
             * todo handleCallbackMap 释放方案
             * 这里使用了全局单例的平台对象，所以该对象被长期持有也并不会造成其他oom问题。
             * 而对外扩展使用了 MAP 回调，是需要释放的，但由于使用范围是在拦截器中，拦截器目前设计也是全局单例的，故并不会造成OOM
             * 但是为了健壮性，还是需要设计一个回收 handleCallbackMap 的方案
             */

            // 获取sdk实例
            authHelper = PhoneNumberAuthHelper.getInstance(context, this);
            // 设置API密钥
            authHelper.setAuthSDKInfo(SECRET_KEY);

            // 开启调试模式，否则会报手机终端不安全60005
//            authHelper.getReporter().setLoggerEnable(true);
            // 设置UI样式配置
            authHelper.setAuthUIConfig(uiConfig);
            // 添加原生控件单击事件响应
            authHelper.setUIClickListener(this::onUIControlClick);
            // 添加自定义布局控件
            addCustomLayout();
        }
        return this;
    }
    //endregion #初始化一键登录相关设置 end

    //region    #封装委托一键登录SDK begin

    /**
     * 加速认证本机号码, 通过调用这个方法来使真实验证调用是得到加速，预先处理掉一部分逻辑
     * <p>推荐在登录⻚初始化的时候调⽤，如果没有合适的调⽤时机不调⽤此接⼝也没关系：</p>
     * <pre>
     * 千万不要APP冷启动初始化就调⽤！！！！
     * 不要调⽤完预取号后⻢上调⽤getVerifyToken！！！
     * 最好判断⽤户是否登录，若已登录不要使⽤此接
     * </pre>
     * @param overdueTime 过期时间
     * @return
     */
    public AliQuickLoginPlatform accelerateVerify(int overdueTime) {
        authHelper.accelerateVerify(overdueTime, new PreLoginResultListener() {
            @Override
            public void onTokenSuccess(String vendor) {
                System.out.println("一键登录：：加速验证成功，运营商：" + vendor);
            }

            @Override
            public void onTokenFailed(String vendor, String failedJson) {
                System.out.println("一键登录：：加速验证失败，运营商：" + vendor + "---- 详细：" + failedJson);
            }
        });
        return this;
    }

    /**
     * 获取本机验证token，结果会通过 TokenResultListener 返回，
     * 这里可以在不拉起授权页的状态下直接获得一键登录的token。
     * 但目前不知道是否需要前置条件才能获得，所以流程依旧走拉起授权页再一键登录，
     * 如果可以在任何时候获得，那完全可以不拉起授权页通过自定义页面来完成一键登录
     * @param totalTimeout 超时总时长
     * @return
     */
    public AliQuickLoginPlatform getVerifyToken(int totalTimeout) {
        authHelper.getVerifyToken(totalTimeout);
        return this;
    }

    /**
     *  在每次SIM卡变化的时候都检查一下一键登录的状态是否可以支持，可以添加用户是否已登录判定，但相应的如果用户退出后应该也需要出发此方法
     */
    public void checkAccelerateLogin() {
        String vendor = getCurrentCarrierName();
        System.out.println("一键登录：：加速验证，运营商：" + vendor);
        if(vendor != null && vendor.length() > 0 && !"unknown".equals(vendor)) {
            postDelayed(() -> accelerateLoginPage(3000), 1000);
        }
    }
    /**
     * 加速授权⻚唤起，通过调用这个方法来使授权页唤起得到加速，否则在登录时可能因为网络或其他环境原因引起处理卡顿，这里会预先处理环境因素。
     * <p>推荐在登录⻚初始化的时候调⽤，如果没有合适的调⽤时机不调⽤此接⼝也没关系：</p>
     * <pre>
     * 千万不要APP冷启动初始化就调⽤！！！！
     * 不要调⽤完预取号后⻢上调⽤getLoginToken！！！
     * 最好判断⽤户是否登录，若已登录不要使⽤此接
     * </pre>
     * @param overdueTime
     * @return
     */
    public AliQuickLoginPlatform accelerateLoginPage(int overdueTime) {
        authHelper.accelerateLoginPage(overdueTime, new PreLoginResultListener() {
            @Override
            public void onTokenSuccess(String vendor) {
                System.out.println("一键登录：：加速授权页成功，运营商：" + vendor);
                isSupportQuickLogin = true;
            }

            @Override
            public void onTokenFailed(String vendor, String failedJson) {
                System.out.println("一键登录：：加速授权页失败，运营商：" + vendor + "---- 详细：" + failedJson);
                TokenResultData data = new TokenResultData().parser(failedJson);
                if("600015".equals(data.code) && !"请求超时".equals(data.msg)) {
                    isSupportQuickLogin = false;
                    failedSupportQuickLoginJson = failedJson;
                    return;
                }
            }
        });
        return this;
    }

    /**
     * 检查当前手机环境是否支持手机号码一键登录，只检查手机硬件环境是否支持，
     * 按照SDK文档提供流程最好再 authorize 前做此验证，但是没做好像也没有什么关系。
     * @return
     */
    public AliQuickLoginPlatform checkAvailable() {
        if(!isQuickLogin) {
            authHelper.checkEnvAvailable(PhoneNumberAuthHelper.SERVICE_TYPE_LOGIN);
            isQuickLogin = true;
            postDelayed(() -> isQuickLogin = false, 1000);
        }
        return this;
    }

    /**
     * 唤起授权验证页
     * @param context 当前激活的上下文
     * @param outTimeMillis 请求超时时间设置
     * @return
     */
    public AliQuickLoginPlatform authorize(Context context, int outTimeMillis) {
        if(isSupportQuickLogin) {
            authHelper.getLoginToken(context, outTimeMillis);
        } else {
            onTokenFailed(failedSupportQuickLoginJson);
        }
        return this;
    }
    //endregion #封装委托一键登录SDK end

    /**
     * 注册监听：处理 TokenResultData 返回后处理行为。
     * @param <T>
     */
    public interface HandleCallback<T> {
        void callback(T t);
    }

    // TokenResultListener 返回 TokenResultData 中的 code 说明，只描述有用Code，并用于添加回掉注册的 TAG
    public static final String 终端环境检查成功 = "600024";
    public static final String 获取token成功 = "600000";
    public static final String 跳转登录引导页 = "GOTO_LOGIN";

    /**
     * 平台接口调用 TokenResultListener 返回 json 字串结果对象
     */
    public final class TokenResultData {
        public String carrierFailedResultData;  //:"",
        // 消息代码: 有用的信息用来描述当前是什么操作的结果，可以参见上面的Code描述，并
        public String code;                     //:"600024",
        // 消息描述
        public String msg;                      //:"终端支持认证",
        // 请求代码
        public int    requestCode;              //:0,
        // 请求ID,发起的一次页面唤起全过程这个ID相同
        public String requestId;                //:"8113e4be-e576-41bb-833e-2da87f5d7bfe",
        public String token;
        public String vendorName;               //:"cm_zyhl"

        public TokenResultData parser(String json) {
            if(json == null || json.length() <= 0) {
                return this;
            }
            try {
                JSONObject jsonObj = new JSONObject(json);
                carrierFailedResultData = jsonObj.getString("carrierFailedResultData");
                code = jsonObj.getString("code");
                msg = jsonObj.getString("msg");
                requestCode = jsonObj.getInt("requestCode");
                requestId = jsonObj.getString("requestId");
                token = 获取token成功.equals(code) ? jsonObj.getString("token") : "";
                vendorName = jsonObj.getString("vendorName");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return this;
        }
    }

}
